<?php

/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#namespace tehframework\annotations\builders;

#use tehframework\annotations\DocBlock;
#use tehframework\annotations\DocBlockDefinition;
#use tehframework\annotations\Annotation;
#use tehframework\annotations\AnnotationDefinition;
#use tehframework\annotations\Tag;
#use tehframework\annotations\TagDefinition;
#use tehframework\annotations\FakeAnnotation;
#use tehframework\annotations\tags\UnknownTag;
#use tehframework\testing\phpunit\TestCase;

require_once 'test/testHelper.php';

class DocBlockBuilderImplTest extends TestCase
{
	/**
	 * @var DocBlockBuilderImpl
	 */
	private $builder;
	
	protected function beforeTest()
	{
		$this->builder = new DocBlockBuilderImpl();
	}
	
	function testBuildDocBlockSetsShortDescriptionToNullIfItsEmpty()
	{
		$def = new DocBlockDefinition('');
		
		$this->assertFalse(
			$this->builder->buildDocBlock($def)->hasShortDescription());
	}
	
	function testBuildDocBlockSetsLongDescriptionToNullIfItsEmpty()
	{
		$def = new DocBlockDefinition(null, '');
		
		$this->assertFalse(
			$this->builder->buildDocBlock($def)->hasLongDescription());
	}
	
	function testBuildDocBlockDoesNotBuildTagsIfNoBuilderWasSpecified()
	{
		$def = new DocBlockDefinition(null, null, array(
			new TagDefinition('author', 'me')));
		
		$this->assertFalse($this->builder->buildDocBlock($def)->hasAnyTags());
	}
	
	function testBuildDocBlockDoesNotBuildAnnotationsIfNoBuilderWasSpecified()
	{
		$def = new DocBlockDefinition(null, null, array(), array(
			new AnnotationDefinition('Fake')));
		
		$this->assertFalse(
			$this->builder->buildDocBlock($def)->hasAnyAnnotations());
	}
	
	function testBuildDocBlockCopiesShortDescriptionFromDefinitionIfSpecified()
	{
		$def = new DocBlockDefinition('Short');
		$doc = $this->builder->buildDocBlock($def);
		
		$this->assertTrue($doc->hasShortDescription());
		$this->assertSame($def->shortDescription, $doc->getShortDescription());
	}
	
	function testBuildDocBlockCopiesLongDescriptionFromDefinitionIfSpecified()
	{
		$def = new DocBlockDefinition(null, 'Long');
		$doc = $this->builder->buildDocBlock($def);
		
		$this->assertTrue($doc->hasLongDescription());
		$this->assertSame($def->longDescription, $doc->getLongDescription());
	}
	
	function testBuildDocBlockBuildsSpecifiedTagFromDefinitionUsingGivenTagBuilder()
	{
		$tagDef = new TagDefinition('author', 'me');
		$docDef = new DocBlockDefinition(null, null, array($tagDef));
		
		$tag = new UnknownTag($tagDef);
		
		$tagBuilder = $this->mock('TagBuilder');
		
		$this->expect->one($tagBuilder)->buildTag($tagDef)->returns($tag);
		
		$doc = $this->builder->setTagBuilder($tagBuilder)->buildDocBlock($docDef);
		
		$this->assertSame($tag, $doc->getTag('author'));
	}
	
	function testBuildDocBlockBuildsSpecifiedAnnotationFromDefinitionUsingGivenAnnotationBuilder()
	{
		$annDef = new AnnotationDefinition('Fake');
		$docDef = new DocBlockDefinition(null, null, array(), array($annDef));
		
		$ann = new FakeAnnotation();
		
		$annBuilder = $this->mock('AnnotationBuilder');
		
		$this->expect->one($annBuilder)->buildAnnotation($annDef)->returns($ann);
		
		$doc = $this->builder
			->setAnnotationBuilder($annBuilder)
			->buildDocBlock($docDef);
		
		$this->assertSame($ann, $doc->getAnnotation('Fake'));
	}
	
	function testBuildDocBlockMakesAnnotationAwareOfItsTargetIfTargetIsSpecified()
	{
		$annDef = new AnnotationDefinition('MoreFake');
		$docDef = new DocBlockDefinition(null, null, array(), array($annDef));
		
		$ann = new MoreFakeAnnotation();
		
		$annBuilder = $this->mock('AnnotationBuilder');
		
		$this->expect->one($annBuilder)->buildAnnotation($annDef)->returns($ann);
		
		$doc = $this->builder
			->setAnnotationBuilder($annBuilder)
			->setAnnotationTarget(new ReflectionObject($this))
			->buildDocBlock($docDef);
		
		$this->assertTrue($doc->getAnnotation('MoreFake')->isAwareOfTarget());
	}
	
	function testBuildDocBlockDoesNotMakeAnnotationAwareOfItsTargetIfTargetIsNotSpecified()
	{
		$annDef = new AnnotationDefinition('MoreFake');
		$docDef = new DocBlockDefinition(null, null, array(), array($annDef));
		
		$ann = new MoreFakeAnnotation();
		
		$annBuilder = $this->mock('AnnotationBuilder');
		
		$this->expect->one($annBuilder)->buildAnnotation($annDef)->returns($ann);
		
		$doc = $this->builder
			->setAnnotationBuilder($annBuilder)
			->buildDocBlock($docDef);
		
		$this->assertFalse($doc->getAnnotation('MoreFake')->isAwareOfTarget());
	}
	
	function testBuildDocBlock()
	{
		$tagDefs = array(
			new TagDefinition('author', 'me'),
			new TagDefinition('author', 'you'),
			new TagDefinition('param', 'bool $yesOrNo'));
		$tags = array(
			new UnknownTag($tagDefs[0]),
			new UnknownTag($tagDefs[1]),
			new UnknownTag($tagDefs[2]));
		$annDefs = array(
			new AnnotationDefinition('Fake'),
			new AnnotationDefinition('Fake', 'MoreFake'),
			new AnnotationDefinition('FooBar'));
		$anns = array(
			new FakeAnnotation(),
			new MoreFakeAnnotation(),
			new FooBarAnnotation());
		
		$def = new DocBlockDefinition(
			'Short.', "Long\nDescription.", $tagDefs, $annDefs);
		
		$tagBuilder = $this->mock('TagBuilder');
		$annBuilder = $this->mock('AnnotationBuilder');
	
		foreach ($tagDefs as $k => $tagDef)
		{
			$this->expect->one($tagBuilder)->buildTag($tagDef)->returns($tags[$k]);
		}
		
		foreach ($annDefs as $k => $annDef)
		{
			$this->expect
				->one($annBuilder)->buildAnnotation($annDef)->returns($anns[$k]);
		}
		
		$doc = $this->builder
			->setTagBuilder($tagBuilder)
			->setAnnotationBuilder($annBuilder)
			->buildDocBlock($def);
		
		$this->assertSame($def->shortDescription, $doc->getShortDescription());
		$this->assertSame($def->longDescription, $doc->getLongDescription());
		$this->assertTrue($doc->isTaggedWith('author'));
		$this->assertTrue($doc->isTaggedWith('param'));
		$this->assertTrue($doc->isAnnotatedWith('Fake'));
		$this->assertTrue($doc->isAnnotatedWith('FooBar'));
	}
}